/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.aljfce.business.notification.boundary;

import com.aljfce.business.entity.Event;
import com.aljfce.business.entity.Notification;
import com.aljfce.business.entity.User;
import com.aljfce.business.entity.WeatherInformation;
import com.aljfce.business.event.boundary.EventDetailBean;
import java.io.Serializable;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.http.HttpServletRequest;

/**
 * This class is meant to manage the interaction between a user an the linked
 * notifications.
 *
 *
 * @author Jacopo Ferrigno <jacopo.ferrigno@gmail.com>
 */
@ManagedBean(eager = true)
@ViewScoped
public class NotificationBean implements Serializable {

    /**
     * This constant represent the notification received one day before the
     * event.
     *
     */
    public final String ONE_DAY = Notification.TYPE.ONE_DAY_NOTIFICATION;
    /**
     * This constant represent the notification received by an event creator
     * three day before an event.
     */
    public final String THREE_DAY = Notification.TYPE.THREE_DAY_NOTIFICATION;
    /**
     * This constant represent the notification received upon the invitation to
     * an event.
     */
    public final String INVITATION = Notification.TYPE.INVITATION_NOTIFICATION;
    /**
     * This constant represent the notification received upon the weather
     * forecast for an event has changed.
     */
    public final String WEATHER_CHANGED = Notification.TYPE.WEATHER_CHANGED;

    /**
     * This constant store the default update interval value for the
     * notification list.
     *
     */
    public final Integer UPDATE_INTERVALL = 10;

    private Notification notificationSelected;

    private ArrayList<Notification> notifications = null;

    @Inject
    private User user;

    @PersistenceContext
    EntityManager em;

    @PostConstruct
    public void init() {
        this.notifications = this.buildNotificationList();
    }

    /**
     * This method will return the list of all the notification of the user
     *
     * @return a {@link List} of {@link Notification}
     */
    public List<Notification> getNotifications() {
        return this.notifications;
    }

    /**
     * This method will return the selected notification from the
     * notificaitonList. The returned value could be null due to JSF
     * initialization or because {@link #setNotificationSelected } has not yet
     * been called.
     *
     * @return A {@link Notification} object representing the selected
     * notification
     */
    public Notification getNotificationSelected() {
        return this.notificationSelected;
    }

    /**
     * This method will set the selected notification by the user.
     *
     * @param notificationSelected a {@link Notification} object representing
     * the selected notification
     */
    public void setNotificationSelected(Notification notificationSelected) {
        this.notificationSelected = notificationSelected;

    }

    /**
     * This method will return the weather condition associated to the
     * {@link Event} linked to the selected notification, if there is any.
     * Otherwise will return an empty String.
     *
     * @return A string representing the weather condition associated to the
     * notification
     */
    public String getEventWeather() {
        if (this.notificationSelected == null) {
            return "";
        }
        Event event = this.notificationSelected.getEventId();

        Collection<WeatherInformation> weatherInformationCollection = event.getWeatherInformationCollection();

        if (weatherInformationCollection == null || weatherInformationCollection.isEmpty()) {
            return "";
        }
        ArrayList<WeatherInformation> wi = new ArrayList<>(weatherInformationCollection);
        String description = wi.get(0).getDetails();
        return description;

    }

    /**
     * This method will update the notification list of the user
     *
     */
    public void updateNotifications() {
        this.init();

    }

    /**
     * This method will extract the user mail from the list of users linked to
     * the notification.
     *
     * @param notificationForUser a {@link Notification} object
     * @return the user email if object is not null an the list of users linked
     * to the notification is not empy.
     */
    public String getUserNameFromNotification(Notification notificationForUser) {
        if (notificationForUser == null || !notificationForUser.getType().equals(Notification.TYPE.INVITATION_NOTIFICATION)) {
            return null;
        }
        ArrayList<User> users = new ArrayList<>(notificationForUser.getUserCollection());
        if (users.isEmpty()) {
            return null;
        }
        return users.get(0).getEmail();

    }

    public ArrayList<Notification> getInvitationNotificationForEvent(Event event) {
        if (event == null) {
            return null;
        }
        ArrayList<Notification> invitationNotifications = new ArrayList<>();
        for (Notification notification : event.getNotificationCollection()) {
            if (notification.getType().equals(Notification.TYPE.INVITATION_NOTIFICATION)) {
                invitationNotifications.add(notification);
            }
        }
        return invitationNotifications;
    }

    /**
     * This method will return a pre-formatted text for the notification passed
     * as argument. It will automatically fetch linked information to the
     * notification if possible or an empty string if any problems occur.
     *
     * @param notification the notification for which build the text
     * @return
     */
    public String getNotificationText(Notification notification) {
        if (notification == null) {
            return "";
        }
        //FIXME This should not be needed
        if (notification.getType() == null) {
            return "Notification: " + notification.getId();
        }
        String eventTitle = notification.getEventId().getTitle();

        switch (notification.getType()) {
            case Notification.TYPE.INVITATION_NOTIFICATION:
                return "You have been invited to " + eventTitle;
            case Notification.TYPE.WEATHER_CHANGED:
                return "The forecast change for event " + eventTitle;
            case Notification.TYPE.ONE_DAY_NOTIFICATION:
                ArrayList<WeatherInformation> wi1 = new ArrayList<>(notification.getEventId().getWeatherInformationCollection());
                return "Warning " + wi1.get(0).getDetails() + " for tomorrow event " + eventTitle;
            case Notification.TYPE.THREE_DAY_NOTIFICATION:
                ArrayList<WeatherInformation> wi3 = new ArrayList<>(notification.getEventId().getWeatherInformationCollection());
                return "Warning " + wi3.get(0).getDetails() + " for in three day event " + eventTitle;
            default:
                return "Notification: ";
        }

    }

    private ArrayList<Notification> buildNotificationList() {
        FacesContext contex = FacesContext.getCurrentInstance();
        HttpServletRequest request = (HttpServletRequest) contex.getExternalContext().getRequest();
        Principal principal = request.getUserPrincipal();
        //This should not appen but just in case
        if (principal == null) {
            return new ArrayList<>();
        }

        this.user = em.find(User.class, principal.getName());

        if (user == null) {
            return new ArrayList<>();
        }

        ArrayList<Notification> notificationsList = new ArrayList<>(user.getNotificationCollection());
        Collections.sort(notificationsList, (Notification n1, Notification n2) -> n1.getSendingDate().compareTo(n2.getSendingDate()));

        return notificationsList;
    }

    /**
     * This method will return the type of the notification
     *
     * @return A string representing the type of the notification
     */
    public String getEventType() {

        if (notificationSelected == null) {
            return null;
        }
        switch (notificationSelected.getType()) {
            case Notification.TYPE.INVITATION_NOTIFICATION:
                return EventDetailBean.INVITED_EVENT;

            case Notification.TYPE.WEATHER_CHANGED:
                if (this.user.getEmail().equals(notificationSelected.getEventId().getCreator().getEmail())) {
                    return EventDetailBean.OWN_EVENT;
                }
                return EventDetailBean.IN_CALENDAR_EVENT;

            case Notification.TYPE.ONE_DAY_NOTIFICATION:
                if (this.user.getEmail().equals(notificationSelected.getEventId().getCreator().getEmail())) {
                    return EventDetailBean.OWN_EVENT;
                }
                return EventDetailBean.IN_CALENDAR_EVENT;

            case Notification.TYPE.THREE_DAY_NOTIFICATION:
                return EventDetailBean.OWN_EVENT;

            default:
                return "";

        }

    }

    //Getter for constant.
    /**
     * This method will return the value of the constant {@link #ONE_DAY}. It is
     * necessary because JSF page have problems accessing constants.
     *
     * @return {@link #ONE_DAY} constant
     */
    public String getONE_DAY() {
        return ONE_DAY;
    }

    /**
     * This method will return the value of the constant {@link #THREE_DAY}. It
     * is necessary because JSF page have problems accessing constants.
     *
     * @return {@link #THREE_DAY} constant
     */
    public String getTHREE_DAY() {
        return THREE_DAY;
    }

    /**
     * This method will return the value of the constant {@link #INVITATION}. It
     * is necessary because JSF page have problems accessing constants.
     *
     * @return {@link #INVITATION} constant
     */
    public String getINVITATION() {
        return INVITATION;
    }

    /**
     * This method will return the value of the constant
     * {@link #WEATHER_CHANGED}. It is necessary because JSF page have problems
     * accessing constants.
     *
     * @return {@link #WEATHER_CHANGED} constant
     */
    public String getWEATHER_CHANGED() {
        return WEATHER_CHANGED;
    }

    /**
     * This method will return the value of the constant
     * {@link #UPDATE_INTERVALL}. It is necessary because JSF page have problems
     * accessing constants.
     *
     * @return {@link #UPDATE_INTERVALL} constant
     */
    public Integer getUPDATE_INTERVALL() {
        return UPDATE_INTERVALL;
    }

}
